// Copyright 2006 Bret Taylor
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

%{

#include <stdlib.h>
#include <iostream>
#include <vector>
#include <string>

#include "ast.h"
#include "filelocation.h"
#include "log.h"

using namespace indie;
using std::vector;
using std::string;

// Global variables
static const FileLocationFactory* g_factory = NULL;
static Log* g_log = NULL;
static AstFile** g_result = NULL;

// Error handler is defined below.
void yyerror(char* message);

// Defined in the lexer-generated source file.
int yylex(void);

%}

// Our definition of yylval, which is the structure through which Lex
// communicates to us about token values.
%union {
    indie::Ast* Tree;

    indie::AstType* Type;
    indie::AstClassType* ClassType;
    indie::AstArrayType* ArrayType;
    indie::AstUnionType* UnionType;

    indie::AstFile* File;
    indie::AstClass* Class;
    indie::AstMethod* Method;
    indie::AstVariable* Variable;
    indie::ClassMembers* ClassMembers;

    indie::AstStatement* Statement;
    indie::AstVariableDeclaration* VariableDeclaration;
    indie::AstAssignment* Assignment;
    indie::AstBlockStatement* BlockStatement;
    indie::AstIf* If;
    indie::AstWhile* While;
    indie::AstFor* For;
    indie::AstReturn* Return;
    indie::AstBreak* Break;

    indie::AstExpression* Expression;
    indie::AstLValue* LValue;
    indie::AstAccess* Access;
    indie::AstArrayAccess* ArrayAccess;
    indie::AstParenthesizedExpression* ParenthesizedExpression;
    indie::AstBinaryExpression* BinaryExpression;
    indie::BinaryOperator BinaryOperator;
    indie::AstUnaryExpression* UnaryExpression;
    indie::UnaryOperator UnaryOperator;
    indie::AstCall* Call;
    indie::AstNewInstance* NewInstance;
    indie::AstNewArray* NewArray;
    indie::AstThis* This;
    indie::AstTypeTest* TypeTest;

    indie::AstIntegerConstant* IntegerConstant;
    indie::AstFloatConstant* FloatConstant;
    indie::AstStringConstant* StringConstant;
    indie::AstBooleanConstant* BooleanConstant;

    std::string* String;

    std::vector<indie::AstStatement*>* StatementList;
    std::vector<indie::AstExpression*>* ExpressionList;
    std::vector<indie::AstClass*>* ClassList;
    std::vector<indie::AstVariable*>* VariableList;
    std::vector<indie::AstType*>* TypeList;
}

// Constants
%token <IntegerConstant>T_IntegerConstant
%token <FloatConstant>T_FloatConstant
%token <StringConstant>T_StringConstant
%token <String>T_Identifier

// Keywords
%token T_Class T_Method T_Extends T_For T_While T_If T_Return T_Break T_New
%token T_Is T_True T_False T_This

// Operators
%token T_And T_Or T_Equal T_NotEqual T_LessEqual T_GreaterEqual T_Increment
%token T_Decrement

%type<Class> Class
%type<ClassType> OptSuper
%type<ClassList> ClassList
%type<Method> Method
%type<UnionType> OptType
%type<ClassMembers> ClassMembers
%type<Variable> Variable
%type<VariableList> VariableList
%type<Type> Type
%type<ClassType> ClassType
%type<ArrayType> ArrayType
%type<UnionType> UnionType
%type<TypeList> TypeList

%type<Statement> Statement
%type<Assignment> Assignment;
%type<BlockStatement> BlockStatement
%type<StatementList> StatementList
%type<Statement> SimpleStatement
%type<VariableDeclaration> VariableDeclaration
%type<If> If
%type<For> For
%type<While> While
%type<Return> Return
%type<Break> Break

%type<Expression> Expression
%type<LValue> LValue
%type<Access> Access
%type<ArrayAccess> ArrayAccess
%type<ParenthesizedExpression> ParenthesizedExpression
%type<BinaryExpression> BinaryExpression
%type<BinaryOperator> BinaryOperator
%type<UnaryExpression> UnaryExpression
%type<UnaryOperator> UnaryPreOperator
%type<Call> Call
%type<NewInstance> NewInstance
%type<NewArray> NewArray
%type<This> This
%type<TypeTest> TypeTest
%type<ExpressionList> ExpressionList
%type<UnaryOperator> UnaryPostOperator
%type<BooleanConstant> BooleanConstant

// Precedence rules
%left T_Is
%left T_Or
%left T_And 
%nonassoc T_Equal T_NotEqual
%nonassoc T_LessEqual T_GreaterEqual '<' '>'
%left  '+' '-'
%left  '*' '/' '%'
%nonassoc T_UnaryMinus '!'
%nonassoc T_Increment T_Decrement
%nonassoc '[' '.' ']'


%%

File: ClassList {
    *g_result = new AstFile(NULL, $1);
};

ClassList: ClassList Class {
    $1->push_back($2);
    $$ = $1;
} | /* Delta */ {
    $$ = new vector<AstClass*>;
};

Class: T_Class T_Identifier OptSuper '{' ClassMembers '}' {
    $$ = new AstClass(g_factory->create(@1, @5), $2, $3, $5->methods,
		      $5->variables);
    delete $5;
};

OptSuper: T_Extends ClassType {
    $$ = $2;
} | /* Delta */ {
    $$ = NULL;
};

ClassMembers: ClassMembers Method {
    $1->methods->push_back($2);
    $$ = $1;
} | ClassMembers Variable ';' {
    $1->variables->push_back($2);
    $$ = $1;
} | /* Delta */ {
    $$ = new ClassMembers;
    $$->methods = new vector<AstMethod*>;
    $$->variables = new vector<AstVariable*>;
};

Method: T_Method T_Identifier '(' VariableList ')' OptType BlockStatement {
    $$ = new AstMethod(g_factory->create(@1, $7), $2, $4, $6, $7);
};

OptType: ':' UnionType {
    $$ = $2;
} | /* Delta */ {
    $$ = NULL;
};

VariableList: VariableList ',' Variable {
    $1->push_back($3);
    $$ = $1;
} | Variable {
    $$ = new vector<AstVariable*>;
    $$->push_back($1);
} | /* Delta */ {
    $$ = new vector<AstVariable*>;
};

Variable: T_Identifier ':' UnionType {
    $$ = new AstVariable(g_factory->create(@1, $3), $1, $3);
};

UnionType: TypeList {
    $$ = new AstUnionType(g_factory->create((*$1)[0]), $1);
};

TypeList: TypeList Type {
    $1->push_back($2);
    $$ = $1;
} | Type {
    $$ = new vector<AstType*>;
    $$->push_back($1);
};

Type: ClassType {
    $$ = $1;
} | ArrayType {
    $$ = $1;
};

ClassType: T_Identifier {
    $$ = new AstClassType(g_factory->create(@1), $1);
};

ArrayType: Type '[' ']' {
    $$ = new AstArrayType(g_factory->create($1, @3), $1);
};

BlockStatement: '{' StatementList '}' {
    $$ = new AstBlockStatement(g_factory->create(@1, @3), $2);
};

StatementList: StatementList Statement {
    $1->push_back($2);
    $$ = $1;
} | /* Delta */ {
    $$ = new vector<AstStatement*>;
};

Statement: BlockStatement {
    $$ = $1;
} | If {
    $$ = $1;
} | While {
    $$ = $1;
} | For {
    $$ = $1;
} | Return {
    $$ = $1;
} | Break {
    $$ = $1;
} | SimpleStatement ';' {
    $$ = $1;
};

SimpleStatement: Expression {
    $$ = new AstExpressionStatement(g_factory->create($1), $1);
} | VariableDeclaration {
    $$ = $1;
} | Assignment {
    $$ = $1;
} | /* Delta */ {
    $$ = new AstEmptyStatement(NULL);
};

If: T_If '(' Expression ')' Statement {
    $$ = new AstIf(g_factory->create(@1, $5), $3, $5, NULL);
};

While: T_While '(' Expression ')' Statement {
    $$ = new AstWhile(g_factory->create(@1, $5), $3, $5);
};

For: T_For '(' SimpleStatement ';' Expression ';' SimpleStatement ')'
     Statement {
    $$ = new AstFor(g_factory->create(@1, $5), $3, $5, $7, $9);
};

Break: T_Break ';' {
    $$ = new AstBreak(g_factory->create(@1));
};

Return: T_Return Expression ';' {
    $$ = new AstReturn(g_factory->create(@1, $2), $2);
} | T_Return ';' {
    $$ = new AstReturn(g_factory->create(@1), NULL);
};

VariableDeclaration: Variable '=' Expression {
    $$ = new AstVariableDeclaration(g_factory->create($1, $3), $1, $3);
} | Variable {
    $$ = new AstVariableDeclaration(g_factory->create($1), $1, NULL);
};

Assignment: LValue '=' Expression {
    $$ = new AstAssignment(g_factory->create($1, $3), $1, $3);
};

Expression: LValue {
    $$ = $1;
} | Call {
    $$ = $1;
} | T_IntegerConstant {
    $$ = $1;
} | T_FloatConstant {
    $$ = $1;
} | T_StringConstant {
    $$ = $1;
} | BooleanConstant {
    $$ = $1;
} | ParenthesizedExpression {
    $$ = $1;
} | NewInstance {
    $$ = $1;
} | NewArray {
    $$ = $1;
} | This {
    $$ = $1;
} | TypeTest {
    $$ = $1;
} | BinaryExpression {
    $$ = $1;
} | UnaryExpression {
    $$ = $1;
};

Call: Access '(' ExpressionList ')' {
    $$ = new AstCall(g_factory->create($1, @4), $1, $3);
};

ExpressionList: ExpressionList ',' Expression {
    $1->push_back($3);
    $$ = $1;
} | Expression {
    $$ = new vector<AstExpression*>;
    $$->push_back($1);
} | /* Delta */ {
    $$ = new vector<AstExpression*>;
};

NewInstance: T_New Type '(' ExpressionList ')' {
    $$ = new AstNewInstance(g_factory->create(@1, @5), $2, $4);
};

NewArray: T_New Type '[' Expression ']' {
    $$ = new AstNewArray(g_factory->create(@1, @5), $2, $4);
};

This: T_This {
    $$ = new AstThis(g_factory->create(@1));
};

TypeTest: Expression T_Is Type {
    $$ = new AstTypeTest(g_factory->create($1, $3), $1, $3);
};

BooleanConstant: T_True {
    $$ = new AstBooleanConstant(g_factory->create(@1), true);
} | T_False {
    $$ = new AstBooleanConstant(g_factory->create(@1), false);
};

BinaryExpression: Expression BinaryOperator Expression {
    $$ = new AstBinaryExpression(g_factory->create($1, $3), $1, $2, $3);
};

BinaryOperator: '+' {
    $$ = Addition;
} | '-' {
    $$ = Subtraction;
} | '/' {
    $$ = Division;
} | '*' {
    $$ = Multiplication;
} | '%' {
    $$ = Mod;
} | '<' {
    $$ = LessThan;
} | '>' {
    $$ = GreaterThan;
} | T_And {
    $$ = And;
} | T_Or {
    $$ = Or;
} | T_Equal {
    $$ = Equal;
} | T_NotEqual {
    $$ = NotEqual;
} | T_LessEqual {
    $$ = LessEqual;
} | T_GreaterEqual {
    $$ = GreaterEqual;
};

UnaryExpression: '-' Expression %prec T_UnaryMinus {
    $$ = new AstUnaryExpression(g_factory->create(@1, $2), $2, Negative);
} | UnaryPreOperator Expression {
    $$ = new AstUnaryExpression(g_factory->create(@1, $2), $2, $1);
} | Expression UnaryPostOperator {
    $$ = new AstUnaryExpression(g_factory->create(@2, $1), $1, $2);
};

UnaryPreOperator: '!' {
    $$ = Not;
} | T_Increment {
    $$ = PreIncrement;
} | T_Decrement {
    $$ = PreDecrement;
};

UnaryPostOperator: T_Increment {
    $$ = PostIncrement;
} | T_Decrement {
    $$ = PostDecrement;
};

LValue: Access {
    $$ = $1;
} | ArrayAccess {
    $$ = $1;
};

Access: Expression '.' T_Identifier {
    $$ = new AstAccess(g_factory->create($1, @3), $1, $3);
} | T_Identifier {
    $$ = new AstAccess(g_factory->create(@1), NULL, $1);
};

ArrayAccess: Expression '[' Expression ']' {
    $$ = new AstArrayAccess(g_factory->create($1, @4), $1, $3);
};

ParenthesizedExpression: '(' Expression ')' {
    $$ = new AstParenthesizedExpression(g_factory->create(@1, @3), $2);
};


%%

// Initializes our parser for a single file
void InitializeParser(const FileLocationFactory& factory, Log& log,
		      AstFile** result) {
    g_factory = &factory;
    g_log = &log;
    g_result = result;

    yydebug = false;
    yylloc.first_line = 1;
    yylloc.first_column = 1;
    yylloc.last_line = 1;
    yylloc.last_column = 1;
}


// We just call our standard error-logger from this method.
void yyerror(char *message) {
    auto_ptr<FileLocation> loc(g_factory->create(yylloc));
    g_log->Error(*loc, "%s", message);
}
